//
//  EDNet.swift
//  Project
//
//  Created by chenfanfang on 2020/10/11.
//  Copyright © 2020 chenfanfang. All rights reserved.
//

import CryptoSwift
import UIKit
import SwifterSwift
import SwiftyJSON
import AFNetworking

typealias APR_NetSuccessClosure = (_ response: APR_NetResponse) -> Void
typealias APR_NetFailureClosure = (_ response: APR_NetResponse) -> Void

enum APR_NetFileType {
    /// 日志
    case log(data: Data)
}
enum APR_ApiType {
    case Get
    case Post
}
class APR_Net: NSObject {
    private static let publicHeaderParams: [String: String] = {
        var params = [String: String]()
        params["phoneDeviceId"] = APR_DeviceInfo.func_phoneDeviceId()
        params["appBundleId"] = APR_AppInfo.func_appBundleId()
        params["phoneModel"] = APR_DeviceInfo.func_phoneModel()
        params["appVersion"] = APR_AppInfo.func_appVersion()
        params["phoneOsVersion"] = APR_DeviceInfo.func_phoneOsVersion()
        params["phoneOsType"] = APR_DeviceInfo.func_phoneOsType()
        return params
    }()
    static let sessionManager: AFHTTPSessionManager = {
        let manager = AFHTTPSessionManager(baseURL: nil)
        var jsonRequestSerializer = AFJSONRequestSerializer()
        jsonRequestSerializer.timeoutInterval = 15
        jsonRequestSerializer.setValue("application/json", forHTTPHeaderField: "Content-Type")
        manager.requestSerializer = jsonRequestSerializer
        var serializer = AFJSONResponseSerializer()
        var types = serializer.acceptableContentTypes ?? Set()
        types.insert("text/plain")
        types.insert("text/html")
        serializer.acceptableContentTypes = types
        manager.responseSerializer = serializer
        manager.securityPolicy.allowInvalidCertificates = true
        manager.securityPolicy.validatesDomainName = false
        return manager
    }()
}

extension APR_Net {
    
    /// 开始发起请求
    /// - Parameters:
    ///   - subUrl: 子链接
    ///   - apiType: 请求类型
    ///   - params: 请求参数
    ///   - uploadFileData: 如果是上传数据，则里传入需要上传数据的二进制
    ///   - success: 成功的回调
    ///   - failure: 失败的回调
    ///
    static func request(subUrl: String, apiType: APR_ApiType, params: [String: Any]?, uploadFileData: Data?, success: @escaping APR_NetSuccessClosure, failure: @escaping APR_NetFailureClosure) {
        var subUrlString = subUrl
        if subUrlString.hasPrefix("/") {
            subUrlString.removeFirst()
        }
        let url = APR_Client.shared.domain + subUrlString
        let header = getHeader()
        let requestId = header["requestId"] ?? ""
        /// 发起请求的时间戳，单位：毫秒
        let timeStamp = Date().timeIntervalSince1970 * 1000.0
        //=======================
        //     成功的回调
        //=======================
        let successClosure = { (task: URLSessionDataTask, response: Any?) in
            DispatchQueue.global().async {
                /// 花费的时间，单位：毫秒
                let spendTime = Date().timeIntervalSince1970 * 1000.0 - timeStamp
                //=======================
                //       添加日志
                //=======================
                guard let rspDict = response as? [String: Any], let dataString = rspDict["data"] as? String else {
                    APR_LogManager.addLog(logType: .response, messages: [
                        APR_LogMessage(key: .api, value: url),
                        APR_LogMessage(key: .spendTime, value: "\(spendTime.int)毫秒"),
                        APR_LogMessage(key: .requestId, value: requestId),
                        APR_LogMessage(key: .data, value: "数据为空数据")
                    ])
                    let model: APR_NetResponse = APR_NetResponse(errorMsg: "数据为空，请联系客服", errorCode: .emptyError)
                    DispatchQueue.main.async {
                        failure(model)
                    }
                    return()
                }
                let decriptResult: APR_DecriptResult = self.decrypt(txt: dataString)
                guard let rsp = decriptResult.data else {
                    var logMessage: String = ""
                    var errorCode: ErrorCode = .parseError
                    switch decriptResult.status {
                    case .dataEmpty(let msg):
                        errorCode = .decriptEmpty
                        logMessage = msg
                    case .decriptFail(let msg):
                        errorCode = .decriptError
                        logMessage = msg
                    case .instanceFail(let msg):
                        logMessage = msg
                        break
                    default:
                        break
                    }
                    APR_LogManager.addLog(logType: .response, messages: [
                        APR_LogMessage(key: .api, value: url),
                        APR_LogMessage(key: .spendTime, value: "\(spendTime.int)毫秒"),
                        APR_LogMessage(key: .requestId, value: requestId),
                        APR_LogMessage(key: .message, value: "数据异常"),
                        APR_LogMessage(key: .data, value: logMessage)
                    ])
                    let model = APR_NetResponse(errorMsg: "数据错误[\(errorCode.rawValue)],请联系客服", errorCode: errorCode)
                    DispatchQueue.main.async {
                        failure(model)
                    }
                    return()
                }
                APR_LogManager.addLog(logType: .response, messages: [
                    APR_LogMessage(key: .api, value: url),
                    APR_LogMessage(key: .spendTime, value: "\(spendTime.int)毫秒"),
                    APR_LogMessage(key: .requestId, value: requestId),
                    APR_LogMessage(key: .data, value: rsp.jsonString() ?? "")
                ])
                let responseModel = APR_NetResponse(response: rsp)
                if responseModel.errorCode == nil {
                    DispatchQueue.main.async {
                        success(responseModel)
                    }
                } else {
                    DispatchQueue.main.async {
                        failure(responseModel)
                    }
                }
            }
        }
        //=======================
        //       失败的回调
        //=======================
        let failureClosure = { (task: URLSessionDataTask?, error: Error) in
            //=======================
            //       添加日志
            //=======================
            /// 花费的时间，单位：毫秒
            let spendTime = Date().timeIntervalSince1970 * 1000.0 - timeStamp
            APR_LogManager.addLog(logType: .responseError, messages: [
                APR_LogMessage(key: .api, value: url),
                APR_LogMessage(key: .spendTime, value: "\(spendTime.int)毫秒"),
                APR_LogMessage(key: .requestId, value: requestId),
                APR_LogMessage(key: .error, value: error.localizedDescription)
            ])
            let realError = error as NSError
            if let data = realError.userInfo["com.alamofire.serialization.response.error.data"] as? Data {
                let errorString = String(bytes: data, encoding: .utf8) ?? ""
                print(errorString)
            }
            
            let responseModel = APR_NetResponse(error: error)
            failure(responseModel)
        }
        //=======================
        //       添加日志
        //=======================
        APR_LogManager.addLog(logType: .request, messages: [
            APR_LogMessage(key: .api, value: url),
            APR_LogMessage(key: .requestId, value: requestId),
            APR_LogMessage(key: .header, value: header.jsonString() ?? ""),
            APR_LogMessage(key: .params, value: params?.jsonString() ?? "")
        ])
        switch apiType {
        case .Get:
            sessionManager.requestSerializer.timeoutInterval = 15
            sessionManager.get(url, parameters: params ?? [String : Any](), headers: header, progress: nil, success: successClosure, failure: failureClosure)
        case .Post:
            if (uploadFileData == nil) {
                // 普通的post请求
                sessionManager.requestSerializer.timeoutInterval = 15
                sessionManager.post(url, parameters: params ?? [String : Any](), headers: header, progress: nil, success: successClosure, failure: failureClosure)
                return
            }
            // 上传数据
            var uploadParams: [String: Any]?
            var mineTypes: [String] = []
            var datas: [Data] = []
            var fileNames: [String] = []
            
            mineTypes.append("application/zip")
            datas.append(uploadFileData!)
            uploadParams = [
                "type": 4
            ]
            
            let timeInterval = Date().timeIntervalSince1970
            let randomValue = arc4random()
            let fileName = "\(timeInterval.int)" + "\(randomValue)"
            fileNames.append(fileName)
            
            sessionManager.requestSerializer.timeoutInterval = 1000
            sessionManager.post(url, parameters: uploadParams ?? [String : Any](), headers: header, constructingBodyWith: { (multipartFormData: AFMultipartFormData) in
                for index in 0..<mineTypes.count {
                    let mineType = mineTypes[index]
                    let data = datas[index]
                    let fileName = fileNames[index]
                    multipartFormData.appendPart(withFileData: data, name: "file", fileName: fileName, mimeType: mineType)
                }
            }, progress: nil, success: successClosure, failure: failureClosure)
            
        }
    }
}

//=================================================================
//                         Header
//=================================================================
// MARK: - Header
extension APR_Net {
    static func getHeaderParams() -> [String: String] {
        var params = publicHeaderParams
        params["token"] = APR_Client.shared.delegate?.getToken() ?? ""
        params["userId"] = APR_Client.shared.delegate?.getUserId() ?? ""
        params["nonce"] = "\(arc4random())"
        params["timestamp"] = "\(Int(NSDate().timeIntervalSince1970))"
        return params
    }
    static func getHeader() -> [String: String] {
        var params = getHeaderParams()
        let allKeys: [String] = params.keys.sorted()
        var str = ""
        for key in allKeys {
            let value = params[key] ?? ""
            let appendStr = "\(key)=\(value)"
            if str.isEmpty {
                str = str + appendStr
            } else {
                str = str + "&" + appendStr
            }
        }
        str = str + APR_Client.shared.apiKey
        let sign = str.md5().uppercased()
        params["sign"] = sign
        params["requestId"] = (UUID().uuidString + UUID().uuidString).md5()
        params["apiEncrypt"] = "1"
        return params
    }
}

//=================================================================
//                          解密
//=================================================================
// MARK: - 解密

extension APR_Net {
    /// 解密
    private static func decrypt(txt: String) -> APR_DecriptResult {
        let iv: [UInt8] = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
        let ivData = Data(iv)
        let ivStr = String(data: ivData, encoding: .utf8)!
        guard let aes = try? AES(key: "5036419f0fcb49615b6b775af8b86fd1", iv: ivStr) else {
            let status: APR_DecriptStatus = APR_DecriptStatus.instanceFail(msg: txt)
            return APR_DecriptResult(status: status, data: nil)
        }
        guard let result = try? aes.decrypt(Array(hex: txt)) else {
            let status: APR_DecriptStatus = APR_DecriptStatus.decriptFail(msg: txt)
            return APR_DecriptResult(status: status, data: nil)
        }
        let data = Data(result)
        let json = JSON(data).dictionaryObject
        if json == nil {
            return APR_DecriptResult(status: .decriptFail(msg: txt), data: nil)
        } else {
            return APR_DecriptResult(status: .success, data: json)
        }
    }
}

/// 解密的状态
enum APR_DecriptStatus {
    /// 解密成功
    case success
    /// 创建解密对象失败
    case instanceFail(msg: String)
    /// 解密失败
    case decriptFail(msg: String)
    /// 解密失败（数据为空）
    case dataEmpty(msg: String)
}

private struct APR_DecriptResult {
    /// 解密的状态
    var status: APR_DecriptStatus
    /// 解密的数据，只有在解密成功的时候才有值
    var data: [String: Any]?
}
